﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Net.Sockets;
using System.IO;
using System.Net;
using System.Threading;


namespace CentralServer
{
    class Vehicle
    {
        public string name;
        string address;
        int port;
        int hfov, vfov;
        double heightofcam, frontofcar, turningradius;
        public bool relay;
        public TcpClient relaySlot;

        public Vehicle(string addr, int p, string n, int vf, int hf, double height, double front, double radius)
        {
            name = n;
            address = addr;
            port = p;
            hfov = hf;
            vfov = vf;
            heightofcam = height;
            frontofcar = front;
            turningradius = radius;
        }

        public string Address()
        {
            return address;
        }

        public int Port()
        {
            return port;
        }

        public string RelayChoice()
        {
            if(relay)
            {
                return "RELAY";
            }
            else return "DIRECT";
        }

        public string AsString()
        {
            return address + ":" + port + ":" + name+ ":" + vfov+ ":" + hfov + ":" + heightofcam + ":" + frontofcar + ":" + turningradius;
        }

    }

    class MainServer
    {
        static Queue<Vehicle> availablevehicles = new Queue<Vehicle>();
        static StreamReader carReader;
        static StreamReader UIReader;
        static StreamReader sr;
        static StreamWriter carWriter;
        static StreamWriter UIWriter;
        static StreamWriter sw;

        static TcpListener userlistener = new TcpListener(IPAddress.Any, 9091);
        static TcpListener carlistener1 = new TcpListener(IPAddress.Any, 9092);
        static TcpListener carlistener2 = new TcpListener(IPAddress.Any, 9093);
        static TcpListener carlistener3 = new TcpListener(IPAddress.Any, 9094);
        static TcpClient car1;
        static TcpClient relaySlot1;
        static TcpClient car2;
        static TcpClient car3;

        static NetworkStream videoin, videoout;
        static StreamReader userinput;
        static StreamWriter carout;
    
        static void Main(string[] args)
        {

            new Thread(listenForUsers).Start();
            new Thread(listenForCars).Start();
        }

        static void listenForUsers()
        {
            userlistener.Start();
            while (true)
            {
                try
                {
                    TcpClient userconnection = userlistener.AcceptTcpClient();
                    UIReader = new StreamReader(userconnection.GetStream());
                    UIWriter = new StreamWriter(userconnection.GetStream());

                    if (availablevehicles.Count != 0)
                    {
                        UIWriter.WriteLine(availablevehicles.Count);
                        UIWriter.Flush();
                        for (int i = 0; i < availablevehicles.Count; i++)
                        {
                            UIWriter.WriteLine(availablevehicles.ElementAt(i).name);
                            UIWriter.WriteLine(availablevehicles.ElementAt(i).RelayChoice());
                            UIWriter.Flush();
                        }

                        int choice = int.Parse(UIReader.ReadLine());
                        if (availablevehicles.ElementAt(choice).RelayChoice() == "RELAY")
                        {
                            UIWriter.WriteLine("RELAY");
                            UIWriter.Flush();
                            //need to start relaying
                            videoin = availablevehicles.ElementAt(choice).relaySlot.GetStream();
                            videoout = userconnection.GetStream();
                            userinput = UIReader;
                            carout = new StreamWriter(availablevehicles.ElementAt(choice).relaySlot.GetStream());
                            carout.WriteLine("INITKERMIT");
                            carout.Flush();
                            new Thread(relayIntructions).Start();
                            new Thread(relayVideo).Start();
                        }
                        else
                        {
                            UIWriter.WriteLine("DIRECT");
                            UIWriter.Flush();
                            UIWriter.WriteLine(availablevehicles.ElementAt(choice).AsString());
                            UIWriter.Flush();
                        }
                    }
                    else
                    {
                        UIWriter.WriteLine("0");
                        UIWriter.Flush();
                    }
                }
                catch
                {
                    Console.WriteLine("Error");
                }
            }
        }

        static void relayVideo()
        {
            int errorcount = 0;
            byte[] bytes = new byte[64000];
            while (true)
            {
                try
                {
                    int b = videoin.Read(bytes, 0, 64000);
                    videoout.Write(bytes, 0, b);
                    videoout.Flush();
                    Console.WriteLine("Image Relayed");
                }
                catch
                {
                    errorcount++;
                    Console.WriteLine("Video relay error");
                    if (errorcount > 5)
                    {
                        errorcount = 0;
                        break;
                    }
                }
            }

        }
        static void relayIntructions()
        {
            int errorcount = 0;
            while (true)
            {
                try
                {
                    carout.WriteLine(userinput.ReadLine());
                    carout.Flush();
                    Console.WriteLine("Instruction Relayed");
                }
                catch
                {
                    errorcount++;
                    Console.WriteLine("Instruction relay error");
                    if (errorcount > 5)
                    {
                        errorcount = 0;
                        break;
                    }
                }
            }
            
        }

        static void listenForCars()
        {
            carlistener1.Start();

            while(true)
            {
                Console.WriteLine("Car Listener Running");
                try
                {
                    car1 = carlistener1.AcceptTcpClient();
                    Console.WriteLine("Connected to Car.");
                    carReader = new StreamReader(car1.GetStream());
                    carWriter = new StreamWriter(car1.GetStream());
                    string cardetails = carReader.ReadLine();

                    int port = int.Parse(cardetails.Split(':')[0]);
                    string carname = cardetails.Split(':')[1];
                    int vfov = int.Parse(cardetails.Split(':')[2]);
                    int hfov = int.Parse(cardetails.Split(':')[3]);
                    double camheight = double.Parse(cardetails.Split(':')[4]);
                    double frontofcar = double.Parse(cardetails.Split(':')[5]);
                    double turningradius = double.Parse(cardetails.Split(':')[6]);
                    string caraddress = car1.Client.RemoteEndPoint.ToString().Split(':')[0];

                    Vehicle v = new Vehicle(caraddress, port, carname, vfov, hfov, camheight, frontofcar, turningradius);
                    Console.WriteLine("New car - " + cardetails + " @ " + car1.Client.RemoteEndPoint);
                    if (carReader.ReadLine() == "RELAY")
                    {
                        v.relay = true;
                        v.relaySlot = car1;
                    }
                    else
                    {
                        v.relay = false;
                    }
                    availablevehicles.Enqueue(v);
                    Console.WriteLine("Queue size" + availablevehicles.Count);
                }
                catch(Exception e)
                {
                    Console.WriteLine("Error in adding car");
                }
            }
            
        }

    }
}
